yosys -import

plugin -i ql-iob
plugin -i ql-qlf

yosys -import

# Read VPR cells library
read_verilog -lib -specify $::env(TECHMAP_PATH)/cells_sim.v
# Read device specific cells library
read_verilog -lib -specify $::env(DEVICE_CELLS_SIM)

# Synthesize
synth_quicklogic -family pp3

# Optimize the netlist by adaptively splitting cells that fit into C_FRAG into
# smaller that can fit into F_FRAG.

proc max {a b} {
    if {$a > $b} {
        return $a
    } else {
        return $b
    }
}

proc min {a b} {
    if {$a < $b} {
        return $a
    } else {
        return $b
    }
}

# Returns the required number of C_FRAGs to fit the design
proc get_used_c_frag {} {
    set used_c_frag [get_count -cells t:mux8x0 t:LUT4 t:logic_cell_macro]
    set used_t_frag [get_count -cells t:mux4x0 t:LUT2 t:LUT3]

    set used_c_frag_for_t_frag [expr int(ceil($used_t_frag  / 2.0))]
    return [expr $used_c_frag + $used_c_frag_for_t_frag]
}

# Returns the required number of F_FRAGs to fit the design
proc get_used_f_frag {} {
    return [get_count -cells t:inv t:mux2x0 t:LUT1 t:logic_cell_macro]
}


# Load the plugin that allows to retrieve cell count
yosys plugin -i design_introspection
yosys -import

# Maximum number of LOGIC cells in the device
set max_logic 891

# Target number of LOGIC cells. This is less than max to allow the VPR
# packet to have more freedom.
set target_logic [expr int($max_logic * 0.90)]
puts "PACK: Optimizing for target of $target_logic/$max_logic LOGIC cells"

# LUT3 -> mux2x0 (replace)
set used_c_frag [get_used_c_frag]
if {$used_c_frag > $target_logic} {
    puts "PACK: Device overfitted $used_c_frag / $target_logic"

    # Update
    set required_frags [expr 2 * ($used_c_frag - $target_logic)]
    set used_f_frag [get_used_f_frag]
    set free_f_frag [expr $target_logic - $used_f_frag]

    # Try converting LUT3 to mux2x0
    if {$free_f_frag > 0} {
        puts "PACK: Replacing at most $free_f_frag LUT3 with mux2x0"
        set sel_count [min $required_frags $free_f_frag]
        yosys techmap -map $::env(TECHMAP_PATH)/lut3tomux2.v t:LUT3 %R$sel_count
    }
}

# LUT2 -> mux2x0 (replace)
set used_c_frag [get_used_c_frag]
if {$used_c_frag > $target_logic} {
    puts "PACK: Device overfitted $used_c_frag / $target_logic"
    # Update
    set required_frags [expr 2 * ($used_c_frag - $target_logic)]
    set used_f_frag [get_used_f_frag]
    set free_f_frag [expr $target_logic - $used_f_frag]
    # Try converting LUT2 to mux2x0
    if {$free_f_frag > 0} {
        puts "PACK: Replacing at most $free_f_frag LUT2 with mux2x0"
        set sel_count [min $required_frags $free_f_frag]
        yosys techmap -map $::env(TECHMAP_PATH)/lut2tomux2.v t:LUT2 %R$sel_count
    }
}

# Split mux4x0
set used_c_frag [get_used_c_frag]
if {$used_c_frag > $target_logic} {
    puts "PACK: Device overfitted $used_c_frag / $target_logic"

    # Update
    set required_frags [expr 2 * ($used_c_frag - $target_logic)]
    set used_f_frag [get_used_f_frag]
    set free_f_frag [expr $target_logic - $used_f_frag]

    # Try converting mux4x0 to 3x mux2x0
    if {$free_f_frag >= 3} {
        puts "PACK: Splitting at most $free_f_frag mux4x0 to 3x mux2x0"

        set sel_count [min $required_frags [expr int(floor($free_f_frag / 3.0))]]

        # If there are not enough mux4x0 then map some LUT2 to them (these are
        # actually equivalent)
        set mux4_count [get_count -cells t:mux4x0]
        if {$mux4_count < $sel_count} {
            set map_count [expr $sel_count - $mux4_count]
            puts "PACK: Replacing at most $map_count LUT2 with mux4x0"
            yosys techmap -map $::env(TECHMAP_PATH)/lut2tomux4.v t:LUT2 %R$map_count
        }

        yosys techmap -map $::env(TECHMAP_PATH)/mux4tomux2.v t:mux4x0 %R$sel_count
    }
}

# Split mux8x0
set used_c_frag [get_used_c_frag]
if {$used_c_frag > $target_logic} {
    puts "PACK: Device overfitted $used_c_frag / $target_logic"

    # Update
    set required_frags [expr 2 * ($used_c_frag - $target_logic)]
    set used_f_frag [get_used_f_frag]
    set free_f_frag [expr $target_logic - $used_f_frag]

    # Try converting mux8x0 to 7x mux2x0
    if {$free_f_frag >= 7} {
        puts "PACK: Splitting at most $free_f_frag mux8x0 to 7x mux2x0"
        set sel_count [min $required_frags [expr int(floor($free_f_frag / 7.0))]]
        yosys techmap -map $::env(TECHMAP_PATH)/mux8tomux2.v t:mux8x0 %R$sel_count
    }
}

# Final check
set used_c_frag [get_used_c_frag]
if {$used_c_frag > $target_logic} {
    puts "PACK: Device overfitted $used_c_frag / $target_logic. No more optimization possible!"
}

stat

# Assing parameters to IO cells basing on constraints and package pinmap
if { $::env(PCF_FILE) != "" && $::env(PINMAP_FILE) != ""} {
    quicklogic_iob $::env(PCF_FILE) $::env(PINMAP_FILE)
}

# Write a pre-mapped design
write_verilog $::env(OUT_SYNTH_V).premap.v

# Select all logic_0 and logic_1 and apply the techmap to them first. This is
# necessary for constant connection detection in the subsequent techmaps.
select -set consts t:logic_0 t:logic_1
techmap -map  $::env(TECHMAP_PATH)/cells_map.v @consts

# Map to the VPR cell library
techmap -map  $::env(TECHMAP_PATH)/cells_map.v
# Map to the device specific VPR cell library
techmap -map  $::env(DEVICE_CELLS_MAP)

# opt_expr -undriven makes sure all nets are driven, if only by the $undef
# net.
opt_expr -undriven
opt_clean
setundef -zero -params
stat

# Write output JSON, fixup cell names using an external Python script
write_json $::env(OUT_JSON).org.json
exec $::env(PYTHON3) -m f4pga.utils.quicklogic.yosys_fixup_cell_names $::env(OUT_JSON).org.json $::env(OUT_JSON)

# Read the fixed JSON back and write verilog
design -reset
read_json $::env(OUT_JSON)
write_verilog $::env(OUT_SYNTH_V)

design -reset
exec $::env(PYTHON3) -m f4pga.utils.yosys_split_inouts -i $::env(OUT_JSON) -o $::env(SYNTH_JSON)
read_json $::env(SYNTH_JSON)
yosys -import
opt_clean
write_blif -attr -cname -param \
  -true VCC VCC \
  -false GND GND \
  -undef VCC VCC \
  $::env(OUT_EBLIF)
